package cn.pugle.mianshi.thread1To10;

import static cn.pugle.mianshi.thread1To10.Constant.MAX;

/**
 * <p> Created by tzp on 2017/9/6.
 */
@SuppressWarnings({"Duplicates", "AlibabaRemoveCommentedCode"})
public class PrintToTen0 implements Runnable {
    private volatile int count = 1;

    /**
     * 会出现1324这样的顺序, 加volatile加yield也不解决问题.
     * 原因在于"get, 自增, 打印"这3步操作不原子, 线程1 get到3, 加到4, 打印3
     * 第二步和第三步之间, 线程2可以插进来执行自己的3步, 提前打印了4
     */
    public void run() {
        System.out.println(Thread.currentThread() + " Hello World");

        Thread thread1 = new Thread(() -> {
            while (count <= MAX) {
                if (count % 2 == 0) {
                    //"get, 自增, 打印"
                    System.out.println(Thread.currentThread() + " " + count++);
                }
                Thread.yield();
            }
        });
        thread1.start();

        Thread thread2 = new Thread(() -> {
            while (count <= MAX) {
                if (count % 2 == 1) {
                    System.out.println(Thread.currentThread() + " " + count++);
                }
                Thread.yield();
            }
        });
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 有时会打印到100, 有时会到101. 加yield确实不出现这个问题, 但不可靠.
     * 出现101是因为线程2上一轮打印99增到100, 下一循环判断<=MAX时, 仍用的100判断, 这是线程1已经给加到101了, 线程2就能进入if把101打印了
     * 这个问题不是因为可见性, 所以用volatile也不行. 在if里 加上 && count <= MAX可以解决
     * <p>
     * ---------
     * 这引出另一个问题, 这种不上锁的方案表面上按顺序打印了, 其实会空转好多.
     * <p>
     * 两个线程可能同时不进if, 不可能同时进if做count++出bug的可能性被规避了.
     */
    public void run2() {
        System.out.println(Thread.currentThread() + " Hello World");

        Thread thread1 = new Thread(() -> {
            while (count <= MAX) {
                if (count % 2 == 0) {//偶数
                    System.out.println(Thread.currentThread() + " " + count);
                    count++;
                }
            }
        });
        thread1.start();

        Thread thread2 = new Thread(() -> {
            while (count <= MAX) {
                if (count % 2 == 1) {//奇数
                    System.out.println(Thread.currentThread() + " " + count);
                    count++;
                }
            }
        });
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new PrintToTen0().run();
    }
}
